/*

    tools23d - tools for converting images to 3D formats.
    Copyright (C) 2014  Dave Gorst

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>

#ifdef POSIX
	#include <unistd.h>
#endif

#include "tools23d.h"
#include "system.h"
#include "util.h"
#include "glob-all.h"

/**
Raise a warning and recieve input from UI indicating whether we should continue executing.

@param	always_yes		Boolean - if set assume that the answer to any question is "yes".
@param	warning_message	Printf-style message (with specifiers) giving warning text.
@return	Boolean indicating whether we should continue execution.
@note	Move the UI stuff to cli.
*/

BOOL T3D_RaiseWarning (BOOL * always_yes, char * warning_message, ...)
{
	/* Give warning, check if we're a TTY and if the "--yes" option is enabled, then return a result indicating whether we should continue. */
	va_list args;
	char input_char = 0;
	BOOL continue_exec = TRUE;

	va_start (args, warning_message);
	(void) vfprintf (stderr, warning_message, args);

	if ((!*always_yes) && (T3D_IsInteractive ()))
	{
		/* Query user. Accept y/n (needs to be i18n compatible */

		(void) fprintf (stderr, "Continue (%c/%c/%c)?", YES_CHAR, NO_CHAR, ALWAYS_CHAR);

		while ((input_char != YES_CHAR) && (input_char != NO_CHAR) && (input_char != ALWAYS_CHAR))
			input_char = (char) getchar ();

		fputc (input_char, stderr);
		fputc ('\n', stderr);

		continue_exec = ((input_char == YES_CHAR) || (input_char == ALWAYS_CHAR))? TRUE : FALSE;

		if (input_char == ALWAYS_CHAR) *always_yes=TRUE;
	}
	else
	{
		continue_exec = *always_yes ? TRUE : FALSE;
	}

	va_end (args);

	return continue_exec;
}


/**
Write non-error message out to UI.

@param	stdout_message		Non-error level message.
*/

void T3D_OutputMessage (char * stdout_message, ...)
{
	va_list args;
	va_start (args, stdout_message);
	(void) vfprintf (stdout, stdout_message, args);
	va_end (args);
}


/**
Write error message out to UI.

@param	stderr_message		Error level message.
*/

void T3D_OutputError (char * stderr_message, ...)
{
	va_list args;
	va_start (args, stderr_message);
	(void) vfprintf (stderr, stderr_message, args);
	va_end (args);
}

/**
Take output filename and replace sequence number and date/time
specifiers (prefixed with '%') with their numerical equivalents.

@param	input_filename	Filename to be changed.
@param	sequence_number	Current sequence number
@param	write_dt	Datetime value.
@return	Updated filename or ::NULL
*/

char * T3D_ProcessWriteFilename (char * input_filename, unsigned int sequence_number, time_t write_dt)
{
	static char write_filename [MAX_PATH];
	struct tm my_time;

	unsigned int current_char = 0;
	unsigned int write_char = 0;

	unsigned int write_width = 0;
	BOOL zero_pad = FALSE;

	BOOL escape_sequence = FALSE;
	unsigned int current_width = MAX_PRECISION;

	if (T3D_GetLocaltime (write_dt, &my_time) != t3d_success)
		return NULL;

	while (input_filename [current_char] != '\0')
	{
		if ((input_filename [current_char] != '%') && (!escape_sequence))
			write_filename [write_char++] = input_filename [current_char];
		else
		{
			switch (input_filename [current_char])
			{
			case '%':
				if (!escape_sequence)
					escape_sequence = TRUE;
				else
				{
					escape_sequence = FALSE;
					write_filename [write_char++] = '%';
				}
				break;
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
				if ((input_filename [current_char] == '0') && (write_width == 0))
					zero_pad = TRUE;

				write_width *= 10;

				write_width += (input_filename [current_char] - '0');
				break;

			case 'd':
				/* Add sequence number */
				/* Count down from write width after removing anything larger. */

				escape_sequence = FALSE;
				if (!write_width) write_width = MAX_PRECISION;
				write_char += T3D_WriteNumber (sequence_number, write_filename + write_char, zero_pad, write_width);
				write_width = 0;

				break;
				case 'Y':
					if (!write_width) write_width = 4;
					write_char += T3D_WriteNumber (my_time.tm_year + 1900, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
					break;
				case 'M':
					if (!write_width) write_width = 2;
					write_char += T3D_WriteNumber (my_time.tm_mon, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
					break;
				case 'D':
					if (!write_width) write_width = 2;
					write_char += T3D_WriteNumber (my_time.tm_mday, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
				break;
				case 'h':
					if (!write_width) write_width = 2;
					write_char += T3D_WriteNumber (my_time.tm_hour, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
					break;
				case 'm':
					if (!write_width) write_width = 2;
					write_char += T3D_WriteNumber (my_time.tm_min, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
				break;
				case 's':
					if (!write_width) write_width = 2;
					write_char += T3D_WriteNumber (my_time.tm_sec, write_filename + write_char, TRUE, write_width);
					escape_sequence = FALSE;
					write_width = 0;
					break;
				default:
					escape_sequence = FALSE;
					return (char *) NULL;
				break;
			}
		}

		current_char++;
	}

	write_filename [write_char] = '\0';

	return &write_filename [0];
}

/**
Convert a string giving a picture size into two integers.

@param	input   Input value e.g. "1920x1080".
@param	x   Pointer to output X value.
@param	y   Pointer to output Y value.
@return	Boolean indicating success. ::TRUE if it went OK.
*/

/* TODO - allow us to use a diferent delimiter */

BOOL T3D_ConvertXYToInts (char * input, unsigned int * x, unsigned int * y)
{
	BOOL success = TRUE;

	int str_position = 0;

	int params_left = 2;

	*x = *y = 0;

	/* while ((input [str_position] |= '\0') && (params_left) && (success)) */

	/* Test for \0 mid-string! */

	while ((params_left) && (success))
	{
		/* Check if input is valid */

		if ((input [str_position] >= '0') && (input [str_position] <= '9'))
			switch (params_left)
			{
			case 2:
				*x *= 10;
				*x += input [str_position] - '0';
				break;
			case 1:
				*y *= 10;
				*y += input [str_position] - '0';
				break;

			}
		else if ((input [str_position] == 'X') || (input [str_position] == 'x'))
			params_left--;
		else if (input [str_position] == '\0')
		{
			if (params_left > 1) success = FALSE;

			params_left --;
		}
		else
			success = FALSE;

		str_position++;

	}

	return ((success) && ((!params_left) && ((*x) && (*y)))) ? TRUE : FALSE;
}

/**
Check if a string only contains numeric digits.

@param	checkstring String to check.
@return	Boolean indicating success. ::TRUE if it went OK.
*/

BOOL T3D_IsNumeric (char * checkstring)
{
	BOOL found_illegal = FALSE;

	while ((!found_illegal) && (*checkstring != '\0'))
	{
		if
		(
			(!((*checkstring >= '0') && (*checkstring <= '9'))) &&
			(*checkstring != '.') &&
			(*checkstring != '-')
		)
		{
			found_illegal=TRUE;
		}

		++checkstring;
	}

	return (!found_illegal);
}

/**
Write a number to a string as text.

@param	input   Number to write
@param  writestring Pointer to output buffer.
@param  is_padded   Should string be left padded with zeroes?
@param  write_width Desired width of output.
@return	Length of string written.
*/

unsigned int T3D_WriteNumber (unsigned int input, char * writestring, BOOL is_padded, unsigned int write_width)
{
	unsigned int current_width;
	char reversed [MAX_PRECISION];
	unsigned int chars_written = 0;

	if (!write_width) write_width = MAX_PRECISION;

	write_width = write_width > MAX_PRECISION ? MAX_PRECISION : write_width;

	for (current_width = 0; current_width < write_width; current_width++)
	{
		reversed [write_width - current_width - 1] = input % 10;
		input -= reversed [write_width - current_width - 1];
		input /= 10;
	}

	for (current_width = 0; current_width < write_width; current_width++)
	{
		if ((is_padded) || (reversed [current_width]) || (current_width == (write_width - 1)))
		{
			*writestring++ = reversed [current_width] + '0';
			++chars_written;
		}
	}

	return chars_written;
}

/**
Function used by sort function to compare two strings.

@param	cmp1    First string to compare.
@param  cmp2    Second string to compare.
@return	Zero if match, otherwise a higher or lower number depending on the match.
*/

int T3D_CompareStrings (const void * cmp1, const void * cmp2)
{
	file_entry * list_entry1 = *((file_entry **) cmp1);
	file_entry * list_entry2 = *((file_entry **) cmp2);

	return (strcmp (list_entry1->filepath, list_entry2->filepath));
}

/**
Function used by sort function to compare two timestamps.

@param	cmp1    First timestamp to compare.
@param  cmp2    Second timestamp to compare.
@return	Zero if match, otherwise a higher or lower number depending on the match.
*/

int T3D_CompareTimestamps (const void * cmp1, const void * cmp2)
{
	file_entry * list_entry1 = *((file_entry **) cmp1);
	file_entry * list_entry2 = *((file_entry **) cmp2);

	return (int) (list_entry1->change_time - list_entry2->change_time);
}
